package org.invigorare.auctions;

import static org.invigorare.auctions.data.SaleDataOpener.*;

import org.invigorare.Utils;
import org.invigorare.auctions.comm.DbSyncResult;
import org.invigorare.auctions.comm.DbSynchronizer;
import org.invigorare.auctions.data.AppSettings;
import org.invigorare.auctions.data.EntryDataOpener;
import org.invigorare.auctions.data.LicenseDataOpener;
import org.invigorare.auctions.data.SaleDataOpener;
import org.invigorare.auctions.data.entities.EntryInfo;
import org.invigorare.auctions.data.entities.Licensee;
import org.invigorare.auctions.data.entities.Licensee.LicenseType;
import org.invigorare.auctions.widgets.EntryAdapter;
import org.invigorare.auctions.widgets.EntryDisplayHelper;
import org.invigorare.auctions.widgets.LicenseeAdapter;
import org.invigorare.auctions.widgets.LicenseeDisplayHelper;


import java.util.*;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;

public class AuctionSaleForm extends Activity {
	
	protected static final int REQUEST_EDIT_CONTENTS = 10101;
	
	protected AppSettings settings;
	
	boolean validateFormInput = true;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	
        super.onCreate(savedInstanceState);
        setContentView(R.layout.auction_sale_form);
        settings = new AppSettings(getApplicationContext());
        // TODO: Use Intents to differentiate between NEW or EDIT EXISTING ENTRY requests
        // TODO: Consider moving all the Text Fields to sub-activities for a cleaner look (and to allow this to double as Viewer AND Editor) 
        
        associateFieldVars();
        populateFields();
        setupAutocompletions();
        setupButtons();
    }

    /** Struct containing info about the produce being sold **/
    class SaleContents {
    	final int produceId;
    	final float weight;
    	final float rate;
    	
    	public SaleContents(int produceId, float weight, float rate) {
    		this.produceId = produceId;
    		this.weight = weight;
    		this.rate = rate;
    	}
    	float getTotal() { return weight*rate; }
    }
    
    /** Struct containing info about the fees **/
    class Fees {
    	final float weight, rate, total;
    	
    	public Fees(float weight, float rate, float total) {
    		this.weight = weight;
    		this.rate = rate;
    		this.total = total;
    	}
    	float getTotal() { return weight*rate; }
    }
    
    /** Struct containing info about the entire sale **/
    class SaleSummary {
    	final SaleContents contents;
    	final String sellerId, buyerId;
    	final Fees fees;
    	
    	public SaleSummary(SaleContents contents, String sellerId, String buyerId, Fees fees) {
    		this.contents = contents;
    		this.sellerId = sellerId;
    		this.buyerId = buyerId;
    		this.fees = fees;
    	}
    }
    
    AutoCompleteTextView entryId, tollReceiptNo;
    TextView marketName, employeeInfo, saleDate, departDate;
    View entryInfo, sellerInfo, buyerInfo;
    TextView produceName, produceWeight, produceRate, produceTotalSale;
    SaleContents produceInfo = new SaleContents(-1, 0, 0);  // updated when the subactivity (content editor) returns values
    TextView feeUnload, feeClean, feeWeigh, feeLoad, feePack, feeStitch, commission2agent, commission2market;
    AutoCompleteTextView sellerLicNo, buyerLicNo;
    TableLayout produceTable;
    Button saleDateSetBtn, produceEditBtn, submitButton;
    
    /**
     * Finds the Views of each form field in this form and saves them as variables for easier access.
     */
    private void associateFieldVars() {
    	
    	/* Metadata */
    	marketName = (TextView) findViewById(R.id.saleform_label_market_name);
    	employeeInfo = (TextView) findViewById(R.id.saleform_label_employee_info);
    	entryId = (AutoCompleteTextView) findViewById(R.id.saleform_text_entryId);
    	tollReceiptNo = (AutoCompleteTextView) findViewById(R.id.saleform_text_tollReceiptNo);
    	saleDate = (TextView) findViewById(R.id.saleform_label_sale_date);
    	departDate = (TextView) findViewById(R.id.saleform_label_depart_date);
    	saleDateSetBtn = (Button) findViewById(R.id.saleform_button_sale_date_now);
    	setupEntryInfoFields();
    	
    	/* Seller & Buyer Info */
    	sellerLicNo = (AutoCompleteTextView) findViewById(R.id.saleform_text_seller_license);
    	buyerLicNo  = (AutoCompleteTextView) findViewById(R.id.saleform_text_buyer_license);
    	setupLicenseeInfoFields();
    	
    	/* Auction Contents */
    	produceTable = (TableLayout) findViewById(R.id.saleform_table_items);
    	produceEditBtn = (Button) findViewById(R.id.saleform_button_editContents);
    	produceName = (TextView) findViewById(R.id.saleform_contents_name);
    	produceWeight = (TextView) findViewById(R.id.saleform_contents_weight);
    	produceRate = (TextView) findViewById(R.id.saleform_contents_rate);
    	produceTotalSale = (TextView) findViewById(R.id.saleform_contents_totalSale);
    	
    	/* Fees & Commissions */
    	feeUnload = (TextView) findViewById(R.id.saleform_fees_unload);
    	feeClean = (TextView) findViewById(R.id.saleform_fees_clean);
    	feeWeigh = (TextView) findViewById(R.id.saleform_fees_weigh);
    	feeLoad = (TextView) findViewById(R.id.saleform_fees_load);
    	feePack = (TextView) findViewById(R.id.saleform_fees_pack);
    	feeStitch = (TextView) findViewById(R.id.saleform_fees_stitch);
    	commission2agent = (TextView) findViewById(R.id.saleform_fees_commission_to_agent);
    	commission2market = (TextView) findViewById(R.id.saleform_fees_commission_to_market);
    	
        /* Submit button */
    	submitButton = (Button) findViewById(R.id.saleform_button_submit);
    }
    
    private void populateFields() {
    	Context context = getApplicationContext();
    	AppSettings settings = new AppSettings(context);
    	
    	marketName.setText(settings.getMarketName());
    	employeeInfo.setText(settings.getEmployeeName() + " " + context.getString(R.string.saleform_employee_id_format, settings.getEmployeeId()));
    }
    
    private void setupEntryInfoFields() {
    	entryInfo = findViewById(R.id.saleform_view_entry_info);
        ViewGroup parent = (ViewGroup) entryInfo.getParent();
        int index = parent.indexOfChild(entryInfo);
        parent.removeView(entryInfo);

        LayoutInflater vi = getLayoutInflater();
        entryInfo = vi.inflate(R.layout.entry_list_item, null);
        parent.addView(entryInfo, index);
        entryInfo.setVisibility(View.GONE);
    }
    
    private void setupLicenseeInfoFields() {
    	sellerInfo = findViewById(R.id.saleform_view_seller_info);
        buyerInfo  = findViewById(R.id.saleform_view_buyer_info);

        ViewGroup sellerParent = (ViewGroup) sellerInfo.getParent();
        ViewGroup buyerParent = (ViewGroup) buyerInfo.getParent();
        
        int sellerIndex = sellerParent.indexOfChild(sellerInfo);
        int buyerIndex = buyerParent.indexOfChild(buyerInfo);
        
        sellerParent.removeView(sellerInfo);
        buyerParent.removeView(buyerInfo);
        
        LayoutInflater vi = getLayoutInflater();
        sellerInfo = vi.inflate(R.layout.licensee_list_item, null);
        buyerInfo = vi.inflate(R.layout.licensee_list_item, null);
        
        sellerParent.addView(sellerInfo, sellerIndex);
        buyerParent.addView(buyerInfo, buyerIndex);
    }
    
    Map<String, EntryInfo> entriesMap;
    Map<String, Licensee> sellersMap;
    Map<String, Licensee> buyersMap;
    
    private void setupAutocompletions() {
    	
    	Context context = getApplicationContext();
    	
        updateSaleTimeToNow();
        
        /* Entry AC */
        entriesMap = EntryDataOpener.getEntryIds(context);
        List<EntryInfo> entries = new ArrayList<EntryInfo>(entriesMap.values());
        Log.i("", " Got : " + entries.size() + " entries");
        List<String> entriesIdList = EntryInfo.extractEntryNumbersFromList(entries);
        EntryAdapter entryAdapter = new EntryAdapter(context, R.layout.entry_list_item, entries, entriesIdList);
        entryId.setAdapter(entryAdapter);
        entryId.setThreshold(0);
        entryId.setOnItemClickListener(new EntryClickListener(entriesMap, entryInfo));
        
        /* License AC */
        List<Licensee> licensees = LicenseDataOpener.getLicensees(context);
        sellersMap = Licensee.makeMapFromList(licensees, LicenseType.SELLER_LICENSE);
        buyersMap = Licensee.makeMapFromList(licensees, LicenseType.BUYER_LICENSE);
        
        List<Licensee> sellersList = Licensee.filterByLicenseType(licensees, LicenseType.SELLER_LICENSE);
        List<Licensee> buyersList = Licensee.filterByLicenseType(licensees, LicenseType.BUYER_LICENSE);
        List<String> sellerLicList = Licensee.extractLicenseNumbersFromList(sellersList, LicenseType.SELLER_LICENSE);
        List<String> buyerLicList = Licensee.extractLicenseNumbersFromList(buyersList, LicenseType.BUYER_LICENSE);
        
        LicenseeAdapter sellerAdapter = new LicenseeAdapter(context, R.layout.licensee_list_item_dark, sellersList, sellerLicList, LicenseType.SELLER_LICENSE);
        LicenseeAdapter buyerAdapter = new LicenseeAdapter(context, R.layout.licensee_list_item_dark, buyersList, buyerLicList, LicenseType.BUYER_LICENSE);
        
        sellerLicNo.setAdapter(sellerAdapter);
        buyerLicNo.setAdapter(buyerAdapter);
        sellerLicNo.setThreshold(0);
        buyerLicNo.setThreshold(0);
        
        sellerLicNo.setOnItemClickListener(new LicenseeClickListener(sellersMap, sellerInfo, LicenseType.SELLER_LICENSE));
        buyerLicNo.setOnItemClickListener(new LicenseeClickListener(buyersMap, buyerInfo, LicenseType.BUYER_LICENSE));
    }
    
    private void setupButtons() {
    	
    	saleDateSetBtn.setOnClickListener(new UpdateSaleTimeListener());
    	produceEditBtn.setOnClickListener(new EditContentsListener());
    	submitButton.setOnClickListener(new DbSubmitListener());
    	// TODO: Check data entry & make a "electronic signatures" page
    }
    
    private ContentValues getFormValues() {
    	
    	ContentValues values = new ContentValues();

    	values.put(MARKET_NAME, 	settings.getMarketName());
		values.put(EMPLOYEE_NO, 	settings.getEmployeeId());
		values.put(ENTRY_ID, 		entryId.getText().toString());
		values.put(TOLLRECEIPT_NO, 	tollReceiptNo.getText().toString());
		values.put(SALE_TIMESTAMP, 	saleDate.getText().toString());
		//values.put(DEPART_TIMESTAMP, ""); // leave Departure timestamp null, not used on Android
		
		values.put(SELLER_LICENSE, 	sellerLicNo.getText().toString());
		values.put(BUYER_LICENSE, 	buyerLicNo.getText().toString());
		
		values.put(SALE_PRODUCE_ID, produceInfo.produceId);
		values.put(SALE_WEIGHT,		produceInfo.weight);
		values.put(SALE_RATE,		produceInfo.rate);
		values.put(SALE_TOTAL,		produceInfo.getTotal());
		
		values.put(FEE_UNLOAD,		0); // TODO: don't store 0's
		values.put(FEE_CLEAN,		0);
		values.put(FEE_WEIGH,		0);
		values.put(FEE_LOAD,		0);
		values.put(FEE_STITCH,		0);
		values.put(FEE_PACK,		0);
		values.put(COMMISSION_AGENT, 0);
		values.put(COMMISSION_MANDI, 0);
		
		return values;
    }

    private class LicenseeClickListener implements AdapterView.OnItemClickListener {

    	View licenseeView;
    	LicenseType licenseTypeToDisplay;
    	Map<String, Licensee> licenseeMap;
    	
    	public LicenseeClickListener(Map<String, Licensee> licenseeMap, View licenseeView, LicenseType licenseTypeToDisplay) {
    		this.licenseeMap = licenseeMap;
    		this.licenseeView = licenseeView;
    		this.licenseTypeToDisplay = licenseTypeToDisplay;
    	}
    	
		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			String licNo = (String) parent.getItemAtPosition(position); // assumed LicenseNo
			Log.i("clickL", "Got licenNo: " + licNo);
			Licensee lic = licenseeMap.get(licNo);
			LicenseeDisplayHelper.setLicenseeToDisplay(getApplicationContext(), licenseeView, lic, licenseTypeToDisplay);
		}
    }
    
    private class EntryClickListener implements AdapterView.OnItemClickListener {

    	View entryView;
    	Map<String, EntryInfo> map;
    	
    	public EntryClickListener(Map<String, EntryInfo> entryMap, View entryView) {
    		this.map = entryMap;
    		this.entryView = entryView;
    	}
    	
		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			String entryId = (String) parent.getItemAtPosition(position); // assumed entryId
			Log.i("clickL", "Got entryId: " + entryId);
			EntryInfo entry = map.get(entryId);
			
			entryView.setVisibility(View.VISIBLE);
			EntryDisplayHelper.setEntryToDisplay(getApplicationContext(), entryView, entry);
		}
    }
    
    private void updateSaleTimeToNow() {
    	saleDate.setText(new Date().toString());
    }
    
    private class UpdateSaleTimeListener implements View.OnClickListener {

		public void onClick(View v) {
			AuctionSaleForm.this.updateSaleTimeToNow();
		}
    }
    
    private class EditContentsListener implements View.OnClickListener {

		public void onClick(View v) {
			Intent i = new Intent(AuctionSaleForm.this, AuctionSaleContents.class);
			startActivityForResult(i, REQUEST_EDIT_CONTENTS);
		}
    }
    
    @Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    	super.onActivityResult(requestCode, resultCode, data);
		if(requestCode == REQUEST_EDIT_CONTENTS && resultCode == AuctionSaleContents.SUCCESS_RETURN_CODE){
			Bundle b = data.getExtras();

			int produceId = b.getInt(AuctionSaleContents.KEY_PRODUCE_ID);
			String name = b.getString(AuctionSaleContents.KEY_PRODUCE_NAME);
			float weight = b.getFloat(AuctionSaleContents.KEY_WEIGHT);
			float rate = b.getFloat(AuctionSaleContents.KEY_RATE);

			AuctionSaleForm.this.updateProduceAndPrices(produceId, name, weight, rate);
		}
    }
    
    /**
     * Sets the proper TextViews with the Produce information, and recomputes
     * required monetary fields (on fees and commissions).
     * @param name
     * @param weight
     * @param rate
     */
    protected void updateProduceAndPrices(int produceId, String name, float weight, float rate) {

    	Context c = getApplicationContext();
    	
    	produceInfo = new SaleContents(produceId, weight, rate);
    	float total = produceInfo.getTotal();
    	
    	/* Contents */
		produceName.setText(name);
		produceWeight.setText(Float.toString(weight));
		produceRate.setText(Float.toString(rate));
		produceTotalSale.setText(Float.toString(total));
		
		// Fees and Commissions
		Map<String, Float> feesMap = settings.getCommissionsAndFees();
		
		float feeRateUnload = feesMap.get(AppSettings.FEE_UNLOAD);
		float feeRateClean = feesMap.get(AppSettings.FEE_CLEAN);
		float feeRateWeigh = feesMap.get(AppSettings.FEE_WEIGH);
		float feeRateStitch = feesMap.get(AppSettings.FEE_STITCH);
		float feeRatePack = feesMap.get(AppSettings.FEE_PACK);
		float feeRateLoad = feesMap.get(AppSettings.FEE_LOAD);
		float commRateAgent = feesMap.get(AppSettings.COMMISSION_AGENT);
		float commRateMarket = feesMap.get(AppSettings.COMMISSION_MANDI);

		Utils.setFeeSaleText(c, feeUnload, weight, feeRateUnload);
		Utils.setFeeSaleText(c, feeClean, weight, feeRateClean);
		Utils.setFeeSaleText(c, feeWeigh, weight, feeRateWeigh);
		Utils.setFeeSaleText(c, feeStitch, weight, feeRateStitch);
		Utils.setFeeSaleText(c, feePack, weight, feeRatePack);
		Utils.setFeeSaleText(c, feeLoad, weight, feeRateLoad);
		Utils.setCommissionSaleText(c, commission2agent, total, commRateAgent);
		Utils.setCommissionSaleText(c, commission2market, total, commRateMarket);
    }
    
    private class DbSubmitListener implements View.OnClickListener {
    	
    	public void onClick(View v) {
    		/* Get form data */
    		ContentValues formValues = getFormValues();
    		
    		/* Validate data */
    		if (validateFormInput) {
    			String entryId = (String) formValues.get(ENTRY_ID);
    			String tollId = (String) formValues.get(TOLLRECEIPT_NO);
    			String sellerLic = (String) formValues.get(SELLER_LICENSE);
    			String buyerLic = (String) formValues.get(BUYER_LICENSE);
    			
    			if (!isValidStringInMap(entryId, entriesMap, R.string.saleform_error_needEntryNo)) return;
    			if (!isValidStringInMap(tollId, null, R.string.saleform_error_needTollReceiptNo)) return;
    			if (!isValidStringInMap(sellerLic, null, R.string.saleform_error_needSellerLicense)) return;
    			if (!isValidStringInMap(buyerLic, null, R.string.saleform_error_needBuyerLicense)) return;
    			if (!checkSaleContentsExist(R.string.saleform_error_needContents)) return;
    		}
    		
    		/* Write form data to DB table */
    		SQLiteDatabase db = SaleDataOpener.getWritableDb(AuctionSaleForm.this.getApplicationContext());
    		
    		long rowId = db.insert(
    	            SaleDataOpener.SALE_TABLE_NAME,        	// The table to insert into.
    	            SaleDataOpener.SALE_TABLE_NULL_COL,  	// A hack, SQLite sets this column value to null if values is empty.
    	            formValues                         		// A map of column names, and the values to insert into the columns.
	        );
    		
    		db.close();
    		
    		/* Try outbound update */
    		DbSynchronizer syncer = new DbSynchronizer(AuctionSaleForm.this);
    		DbSyncResult result = syncer.syncDbOutbound();
    		Utils.showToast(AuctionSaleForm.this, "Sent " + result.toString());

    		/* Also secretly inbound update */
    		syncer.syncDbInbound();
    		
    		/* Pop the current Activity */
    		AuctionSaleForm.this.finish();
    	}
    }
    
    /**
     * Invalid if the Map is non-null and non-empty and the string doesn't exist in the map,
     * OR if the string is an empty string.
     * @param s
     * @param map
     * @param errorStrId
     * @return
     */
    private boolean isValidStringInMap(String s, Map<String, ?> map, int errorStrId) {
    	if ((map != null && map.size() != 0 && !map.containsKey(s)) || 
    			s.length() == 0) {
    		Utils.showToast(this, getString(errorStrId));
    		return false;
    	} else {
    		return true;
    	}
    }
    
    private boolean checkSaleContentsExist(int errorStrId) {
		if (produceInfo == null || produceInfo.produceId < 0) {
			Utils.showToast(this, getString(errorStrId));
			return false;
		} else {
			return true;
		}
    }
    
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    	//ignore orientation change
    	super.onConfigurationChanged(newConfig);
    }
    
}